package com.eva.ss.service;

import com.eva.dao.system.model.SystemLoginLog;
import com.eva.framework.captcha.service.ImageCaptchaService;
import com.eva.framework.common.constants.HttpHeaders;
import com.eva.framework.common.constants.ResponseStatus;
import com.eva.framework.common.exception.BusinessException;
import com.eva.framework.common.utils.Utils;
import com.eva.framework.config.AppConfig;
import com.eva.framework.rbac.model.RbacLoginByPasswordDTO;
import com.eva.framework.rbac.service.LoginTokenService;
import com.eva.service.system.SystemLoginLogService;
import com.eva.ss.UserDetailsCache;
import com.eva.ss.model.SsUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

@Slf4j
@Service
public class LoginByPasswordService {

    @Resource
    private AppConfig appConfig;

    @Resource
    private ImageCaptchaService imageCaptchaService;

    @Resource
    private SystemLoginLogService systemLoginLogService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private LoginTokenService loginTokenService;

    @Resource
    private UserDetailsCache userDetailsCache;

    /**
     * 密码登录
     *
     * @param dto 登录参数
     * @return String Token
     */
    public String login (RbacLoginByPasswordDTO dto, HttpServletRequest request, HttpServletResponse response) {
        SystemLoginLog loginLog = new SystemLoginLog();
        loginLog.setLoginUsername(dto.getUsername());
        loginLog.setLoginTime(new Date());
        loginLog.setSystemVersion(appConfig.getVersion());
        loginLog.setIp(Utils.User_Client.getIP(request));
        loginLog.setLocation(Utils.Location.getLocationString(loginLog.getIp()));
        loginLog.setPlatform(Utils.User_Client.getPlatform(request));
        loginLog.setClientInfo(Utils.User_Client.getBrowser(request));
        loginLog.setOsInfo(Utils.User_Client.getOS(request));
        loginLog.setServerIp(Utils.Server.getIP());
        // 校验验证码
        try {
            imageCaptchaService.check(dto.getUuid(), dto.getCode());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            loginLog.setReason(e.getMessage().length() > 200 ? (e.getMessage().substring(0, 190) + "...") : e.getMessage());
            loginLog.setSuccess(Boolean.FALSE);
            systemLoginLogService.create(loginLog);
            throw e;
        }
        try {
            // 登录
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword());
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            SsUserInfo userInfo = (SsUserInfo) authentication.getPrincipal();
            // 填充登录令牌
            String token = loginTokenService.generate();
            userInfo.setToken(token);
            // 将登录信息写入缓存
            userDetailsCache.put(token, userInfo);
            // 创建登录日志
            loginLog.setUserId(userInfo.getId());
            loginLog.setSuccess(Boolean.TRUE);
            systemLoginLogService.create(loginLog);
            // 将token写入cookie
            Cookie cookie = new Cookie(HttpHeaders.TOKEN, token);
            cookie.setHttpOnly(false);
            cookie.setPath("/");
            response.addCookie(cookie);
            return userInfo.getToken();
        } catch (AuthenticationException e) {
            log.error("登录失败", e);
            loginLog.setReason(e.getMessage().length() > 200 ? (e.getMessage().substring(0, 190) + "...") : e.getMessage());
            loginLog.setSuccess(Boolean.FALSE);
            systemLoginLogService.create(loginLog);
            if (e.getCause() instanceof BusinessException) {
                throw (BusinessException) e.getCause();
            }
            throw new BusinessException(ResponseStatus.ACCOUNT_INCORRECT);
        }
    }
}
